﻿// Copyright (c) Inossem. All rights reserved.
// Licensed under the LGPL-3.0 license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
using System.Threading;
using System.Collections.Generic;
using DWF.Studio.Plugin.Contract;
using DWF.Studio.Plugin.Contract.Exceptions;
using DWF.Studio.Plugin.Loader.Event;
using DWF.Studio.Plugin.Loader.Config;
using DWF.Studio.Share.Log;
using System.Threading.Tasks;

namespace DWF.Studio.Plugin.Loader
{
    /// <summary>
    /// �������������
    /// </summary>
    [Serializable]
    public abstract class PluginLoaderBase : MarshalByRefObject
    {
        /// <summary>
        /// ̽�����
        /// </summary>
        public event ProbeCompleteEventHandler ProbeComplete;

        /// <summary>
        /// ������
        /// </summary>
        public event PluginLoadingEventHandler PluginLoading;

        /// <summary>
        /// �������
        /// </summary>
        public event PluginLoadedEventHandler PluginLoaded;

        /// <summary>
        /// ж�����
        /// </summary>
        public event PluginUnloadedEventHandler PluginUnloaded;

        /// <summary>
        /// ̽��
        /// </summary>
        /// <param name="pluginFolderPath"></param>
        /// <param name="scanDeapth"></param>
        /// <returns></returns>
        protected abstract PluginInfoList OnProbe(string pluginFolderPath, SearchOption scanDeapth);

        /// <summary>
        /// ��ȡ��Ϣ
        /// </summary>
        /// <param name="pluginConfig"></param>
        /// <returns></returns>
        protected abstract PluginInfo OnGetInfo(PluginConfig pluginConfig);

        /// <summary>
        /// ����
        /// </summary>
        /// <param name="pluginInfo"></param>
        /// <returns></returns>
        protected abstract PluginBase OnLoad(IPluginInfo pluginInfo);

        /// <summary>
        /// ���캯��
        /// </summary>
        protected PluginLoaderBase()
        {
            this.IsPluginLoaded = false;
            this.EventsEnabled = true;
            this.AssemblyResolutionPaths = new List<string>();

            this.IsValidation = true;
        }

        /// <summary>
        /// ���򼯽���·��
        /// </summary>
        public List<string> AssemblyResolutionPaths
        {
            get;
            internal set;
        }

        /// <summary>
        /// �Ƿ���ɲ������
        /// </summary>
        protected bool IsPluginLoaded
        {
            get;
            set;
        }

        /// <summary>
        /// �¼��Ƿ����
        /// </summary>
        public bool EventsEnabled
        {
            get;
            set;
        }

        /// <summary>
        /// �Ƿ���֤
        /// </summary>
        public bool IsValidation
        {
            get;
            set;
        }

        /// <summary>
        /// ������ڼ���
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnPluginLoading(PluginLoadingEventArgs e)
        {
            if (this.EventsEnabled)
            {
                var evnt = this.PluginLoading;
                if (evnt != null)
                {
                    evnt(this, e);
                }
            }
        }

        /// <summary>
        /// ����������
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnPluginLoaded(PluginLoadedEventArgs e)
        {
            if (this.EventsEnabled)
            {
                var evnt = this.PluginLoaded;
                if (evnt != null)
                {
                    evnt(this, e);
                }
            }
        }

        /// <summary>
        /// ���ж�����
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnPluginUnloaded(PluginUnloadedEventArgs e)
        {
            if (this.EventsEnabled)
            {
                PluginUnloadedEventHandler evnt = this.PluginUnloaded;
                if (evnt != null)
                {
                    evnt(this, e);
                }
            }
        }

        /// <summary>
        /// ̽�����
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnProbeComplete(ProbeCompleteEventArgs e)
        {
            if (this.EventsEnabled)
            {
                var evnt = this.ProbeComplete;
                if (evnt != null)
                {
                    evnt(this, e);
                }
            }
        }

        /// <summary>
        /// ̽����
        /// </summary>
        /// <param name="pluginFolderPath"></param>
        /// <param name="scanDeapth"></param>
        /// <returns></returns>
        public PluginInfoList Probe(string pluginFolderPath, SearchOption scanDeapth = SearchOption.AllDirectories)
        {
            var domain = AppDomain.CreateDomain("DWF.Studio.PluginProbe");//����������
            Type type = this.GetType();
            PluginLoaderBase loader = (PluginLoaderBase)domain.CreateInstanceFromAndUnwrap(type.Assembly.Location, type.FullName);//����
            PluginInfoList infoList = null;
            try
            {
                infoList = loader.OnProbeWrapper(pluginFolderPath, scanDeapth);//̽��
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex);
                throw;
            }
            finally
            {
                AppDomain.Unload(domain);//ж�س�����
            }

            ProbeCompleteEventArgs probeCompleteArgs = new ProbeCompleteEventArgs(infoList);
            this.OnProbeComplete(probeCompleteArgs);

            return infoList;
        }

        public async Task<PluginInfoList> ProbeAsync(string pluginFolderPath, SearchOption scanDeapth = SearchOption.AllDirectories)
        {
            PluginInfoList infoList = null;
            await Task.Run(() =>
            {
                infoList = Probe(pluginFolderPath, scanDeapth);
            });

            return infoList;
        }

        /// <summary>
        /// ̽���װ
        /// </summary>
        /// <param name="pluginFolderPath"></param>
        /// <param name="scanDeapth"></param>
        /// <returns></returns>
        protected PluginInfoList OnProbeWrapper(string pluginFolderPath, SearchOption scanDeapth)
        {
            return this.OnProbe(pluginFolderPath, scanDeapth);
        }

        /// <summary>
        /// ��ȡ��Ϣ��װ
        /// </summary>
        /// <param name="pluginConfig"></param>
        /// <returns></returns>
        protected PluginInfo OnGetInfoWrapper(PluginConfig pluginConfig)
        {
            return this.OnGetInfo(pluginConfig);
        }

        /// <summary>
        /// ����
        /// </summary>
        /// <param name="pluginInfo"></param>
        /// <returns></returns>
        public PluginBase Load(IPluginInfo pluginInfo)
        {
            PluginBase plugin = null;
            PluginLoadingEventArgs loadingArgs = new PluginLoadingEventArgs(pluginInfo.AssemblyFileName, null);
            this.OnPluginLoading(loadingArgs);

            if (!loadingArgs.Cancel)
            {
                plugin = this.OnLoadWrapper(pluginInfo);

                PluginLoadedEventArgs pluginLoadedArgs = new PluginLoadedEventArgs(pluginInfo);
                this.OnPluginLoaded(pluginLoadedArgs);
            }

            return plugin;
        }

        public async Task<PluginBase> LoadAsync(IPluginInfo pluginInfo)
        {
            PluginBase plugin = null;
            await Task.Run(() =>
            {
                plugin = Load(pluginInfo);
            });

            return plugin;
        }

        /// <summary>
        /// ���ذ�װ
        /// </summary>
        /// <param name="pluginInfo"></param>
        /// <returns></returns>
        protected PluginBase OnLoadWrapper(IPluginInfo pluginInfo)
        {
            return this.OnLoad(pluginInfo);
        }
    }
}
